#! /usr/bin/env python
# -*- coding: utf-8 -*-
# iGirl.py 
# it's a bot can control the local system from far away using Gtalk.

import sys, os, subprocess, xmpp
import pdb
# set the master id, which should be the only one can control the local system
MasterID=u"jimmy09140456@gmail.com"

class abbility():
    """this class contant all functions of this bot
    check programs was running,
    check mail,
    check files,
    ..."""
    def __init__(self):
        """initialize, has an attribute path to save the recent directory's path"""
        # sys.path[0] save the current .py file's URI
        self.path = sys.path[0]

    def ls(self):
        """list all files in current path, just like linux's ls command"""
        self.reply = "ls %s \n" % self.path
        dir = os.listdir( self.path )
        for files in dir:
            self.reply += "%s        " % files

    def cd(self):
        """change self.path to path, just like linux's cd command"""
        if len( self.text ) != 2:
            path = os.getenv("HOME")
        else:
            path = self.text[1]
        self.path = path
        self.reply = "cd %s\n" % path

    def downProg(self, prog_name="deluged"):
        """this function can get the `ps -A | grep deluged` 's output"""
        progPopen = subprocess.Popen(args='ps -A | grep %s' % prog_name, shell=True, stdout=subprocess.PIPE)
        out = progPopen.stdout.read()
        if len( out ):
            return "It's running!\n%s" % out
        else:
            return "It's not running!"

    def check(self):
        """this function just call ability.downProg"""
        if len( self.text ) == 1:
            self.reply = "checking deluged\n"
            prog_name = "deluged"
        else:
            prog_name = self.text[1]
            self.reply = "checking %s\n" % prog_name
        self.reply += self.downProg(prog_name)
    
    def kill(self, s = ""):
        """kill a process with pid or the porcess name"""
        # here s was the kill signal, 9 to force kill
        if len( self.text ) > 1:
            pid = self.text[1]
            if pid.isdigit():
                k = "kill"              # use kill because pid is given
            else:
                k = "killall"           # use killall because pid was not given, kill by process name
            if os.system( "%s %s %s" % (k, s, pid) ) == 0:
                self.reply = "the process has been killed"
                self.killed = True      # the kill was success execute, if still Tre with same pid, use signal 9
            else:
                self.killed = False
                self.reply = "No such prcess"
        else:
            self.reply = self.kill.__doc__

    def kill9(self):
        """same as kill but use signal 9"""
        self.kill("-9")

    def run(self):
        """run a program, can run as background. ex. deluged &"""
        if len( self.text ) == 2 and self.text[1][-1] == '&':
            if  os.system( self.text[1] ) == 0:             # return 0 means run success
                self.reply = "Run the program successful"
            else:
                self.reply = "can not run the program !"
        else:
            self.reply = "Please only input the program's name and run it as background\nex. run deluged&"


class logic(abbility):
    """this is the bot's logic, using to create the reply
    current support command:
      help - show this message
      cmd %s - execute the command(%s)
      run deluged & - run deluge's daemon
      kill pid - kill the process
      check - check deluged is running, but will not start deluged if it's not running
      Pid - show the pid of current Bot
      quit - let the bot offline and exit"""
    def __init__(self, client):
        """get the client"""
        abbility.__init__(self)         # reload __init__
        self.client = client

    def getR(self):
        """this function can get the suitable reply """
        if self.text[:3] == u'cmd':
            return self.cmdMode(self.text[4:])
        else:
            try:
                self.text = self.text.split()
                exec( "self.%s()" % self.text[0] )
                return self.reply
            except AttributeError or SyntaxError:
                return "you say: %s" % ' '.join(self.text)
            except:
                raise
                return "I got another Error!"

    def cmdMode(self, command):
        """execute a system command from Master's remote order"""
        # execute the command using os.system
        if command[:4] != u'sudo':
            progPopen = subprocess.Popen(args=command, shell=True, stdout=subprocess.PIPE)
            out = progPopen.stdout.read()
            return "$%s\n%s" % ( command, out )
        else:
            return 'Sorry, Master.\nInvalid command!'

    def help(self):
        """show the help message"""
        self.reply = "Master, the availabie commands:\n"
        self.reply += self.__doc__

    def Pid(self):
        """this function will return the pid of current program"""
        self.reply = str( os.getpid() )

class main():
    _run = True             # control the loop continues or stop
    def __init__(self, argv=[]):
        """class initialize
            it was the main class, no special
            just connect the Gtalk server using jabber ID"""
        # get the pid and save it in /tmp/.pid
        tmp_pid = open("/tmp/.pid", "w")
        print >> tmp_pid, os.getpid()
        tmp_pid.close()
        # check the command line argument, it need the jabber ID & passwd
        if len(argv) < 3:
            print "Usage: %s username@server passwd" % sys.argv[0]
            raise SystemExit

        # OK, connect to the server using the ID with passwd
        # first, processing the jid to get user name, server and passwd
        jid = xmpp.JID(argv[1])
        self.user, self.server, self.passwd = jid.getNode(), jid.getDomain(), argv[2]

        # create the client
        # self.client = xmpp.Client(self.server) 
        self.client = xmpp.Client(self.server, debug=[])  
        self.connectClient()        # connect to the server
        # all OK, register the handles functions
        self.client.RegisterHandler('message', self.message)

        # after connection successful, create the logic class
        self.logic = logic(self.client)
        self.client.send(xmpp.Message(MasterID, "I'm OnLine! Master!"))
        print "Bot Started!"

    def connectClient(self):
        """use self.connect object connect to the server"""
        conres = self.client.connect()
        # check connection is success or fail
        while not conres:
            print "Unable to connect to the server: %s !\nRetry...  " % self.server
            conres = self.client.connect()

        if conres != 'tls':
            print "Warning: unable to estabilish secure connection - TLS failed!"
        # after connect to the server, login with the JID and passwd
        authres = self.client.auth(self.user, self.passwd)
        # check authres
        if not authres:
            print "Unable to login, please check the passwd!"
            raise SystemExit
        if authres != 'sasl':
            print "Warning: unable to perform SASL auth os %s. Old authentication method used! " % self.server
        self.client.sendInitPresence()      # sent the online signal

    def checkOnline(self):
        """check the bot itself is online or not
        using this function to keep the bot alwaly online, if it offline than reconnect"""


    def StepOn(self):
        """this function are using for check when should offline, here type C^C will quit
        a simple from web:
                          while 1:
                       if self.was_first_connected:
                               if self.conn and self.conn.isConnected():
                                       self.conn.Process(1)
                               else:
                                       self.userNotify("reconnecting")
                                       self.xmppReconnect()
                                       sleep(10)
                       else:
                               sleep(1)
        """
        if (not self.client.isConnected()):
            self.connectClient()
            self.client.send(xmpp.Message(MasterID, "I have been reconnected!"))
        try:
            r = self.client.Process(1)
            if ( r == 0 ):
                self.connectClient()
                self.client.send(xmpp.Message(MasterID, "I have been reconnected!"))
        except KeyboardInterrupt:
            self.client.send(xmpp.Message(MasterID, "I have been recive a quit signal from local keyboard !\nI have to OffLine"))
            return 0;
        except IOError:
            self.client.send(xmpp.Message(MasterID, "IOError: Disconnected from server.\nMaybe the connection has problem!"))
        except :
            self.client.send(xmpp.Message(MasterID, "Got other Error!!\nIt should not happen!") )
            raise
        return 1;

    def GoOn(self):
        """this function are using for keep online"""
        while self._run and self.StepOn():
            pass

    def message(self, conn, mess=None):
        """this is the message handler functioin
        the logic of this bot should not be write here"""
        self.logic.text=mess.getBody()     # get the text
        user=mess.getFrom()                # get the user object
        userID = "%s@%s" % (user.getNode(), user.getDomain())   # get the userID
        print "<<(%s) %s" % (userID, self.logic.text)      # print out a log
        if not self.logic.text:
            reply= "I recive a NULL message\nT_T"
        elif userID != MasterID:
            # for the UNknow people, just this
            reply="I don't know you!"
        else:
            # for master
            reply="Master~!\n"
            if self.logic.text == u'quit':
                reply += "Yes Master!\n I will be OffLine"
                self._run = False
            elif self.logic.text == u'reConnect':
                self.client.disconnected()
                self.connectClient()
                print "Bot reFlashed"
                self.reply += "I have been reFlashed ~"
            else:
                reply+=self.logic.getR()

        # send the reply
        try:
            print ">>(%s) %s\n=======================" % (userID, reply)
        except:
            pass
        self.client.send(xmpp.Message(user, reply))

if __name__ == "__main__":
    main = main(sys.argv)
    main.GoOn()
